#include "SQLUnique.h"

#include "../../../utils/instanceof.h"
#include "../../SQLUtils.h"
#include "../expr/SQLMethodInvokeExpr.h"
#include "../../visitor/SQLASTVisitor.h"
#include "../../../utils/listTran.h"
#include "SQLSelectOrderByItem.h"
#include "../../../sql/ast/statement/SQLColumnDefinition.h"
#include "SQLConstraintImpl.h"

SQLUnique::SQLUnique()
{
  indexDefinition = SQLIndexDefinition_ptr(new SQLIndexDefinition());
  indexDefinition->setParent(SharedObject(SQLUnique));
}

// Override name and comment in constraint impl.

SQLName_ptr SQLUnique::getName()
{
  return indexDefinition->getName();
}

void SQLUnique::setName(SQLName_ptr name)
{
  indexDefinition->setName(name);
}

void SQLUnique::setName(string_ptr name)
{
  this->setName(SQLIdentifierExpr_ptr(new SQLIdentifierExpr(name)));
}

SQLExpr_ptr SQLUnique::getComment()
{
  if (indexDefinition->hasOptions())
  {
    return indexDefinition->getOptions()->getComment();
  }
  return NULL;
}

void SQLUnique::setComment(SQLExpr_ptr x)
{
  indexDefinition->getOptions()->setComment(x);
}

SQLIndexDefinition_ptr SQLUnique::getIndexDefinition()
{
  return indexDefinition;
}

SQLSelectOrderByItem_list_ptr SQLUnique::getColumns()
{
  return indexDefinition->getColumns();
}

void SQLUnique::addColumn(SQLExpr_ptr column)
{
  if (column == NULL)
  {
    return;
  }

  addColumn(SQLSelectOrderByItem_ptr(new SQLSelectOrderByItem(column)));
}

void SQLUnique::addColumn(SQLSelectOrderByItem_ptr column)
{
  if (column != NULL)
  {
    column->setParent(SharedObject(SQLUnique));
  }
  indexDefinition->getColumns()->push_back(column);
}

void SQLUnique::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLUnique)))
  {
    acceptChild(visitor, getName());
    SQLObject_list_ptr tmp1 = ListTran::tran_class<SQLSelectOrderByItem, SQLObject>(getColumns());
    acceptChild(visitor, tmp1);
    SQLObject_list_ptr tmp2 = ListTran::tran_class<SQLName, SQLObject>(getCovering());
    acceptChild(visitor, tmp2);
  }
  visitor->endVisit(SharedObject(SQLUnique));
}

BOOL_ptr SQLUnique::containsColumn(string_ptr column)
{
  for (SQLSelectOrderByItem_ptr item : *getColumns())
  {
    SQLExpr_ptr expr = item->getExpr();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      if (SQLUtils::nameEquals((std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->getName(), column))
      {
        return BOOL::TRUE;
      }
    }
  }
  return BOOL::FALSE;
}

BOOL_ptr SQLUnique::containsColumn(long columnNameHash)
{
  for (SQLSelectOrderByItem_ptr item : *getColumns())
  {
    SQLExpr_ptr expr = item->getExpr();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
    {
      if ((std::dynamic_pointer_cast<SQLIdentifierExpr>(expr))->nameHashCode64() == columnNameHash)
      {
        return BOOL::TRUE;
      }
    }
  }
  return BOOL::FALSE;
}

void SQLUnique::cloneTo(SQLUnique_ptr x)
{
  SQLConstraintImpl::cloneTo(std::dynamic_pointer_cast<SQLConstraintImpl>(x));

  indexDefinition->cloneTo(x->indexDefinition);
}

SQLObject_ptr SQLUnique::clone()
{
  SQLObject_ptr clone_tmp = SQLUnique_ptr(new SQLUnique());
  SQLUnique_ptr x = std::dynamic_pointer_cast<SQLUnique>(clone_tmp);
  // SQLUnique *x = new SQLUnique();
  cloneTo(x);
  return x;
}

void SQLUnique::simplify()
{
  // super.simplify();

  for (SQLSelectOrderByItem_ptr item : *getColumns())
  {
    SQLExpr_ptr column = item->getExpr();
    if (instanceof <SQLExpr, SQLIdentifierExpr>(column))
    {
      SQLIdentifierExpr_ptr identExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(column);
      string_ptr columnName = identExpr->getName();
      string_ptr normalized = SQLUtils::normalize(columnName, dbType);
      if (*normalized != *columnName)
      {
        item->setExpr(SQLObject_ptr(new SQLIdentifierExpr(columnName)));
      }
    }
  }
}

BOOL_ptr SQLUnique::applyColumnRename(SQLName_ptr columnName, SQLColumnDefinition *to)
{
  for (SQLSelectOrderByItem_ptr orderByItem : *getColumns())
  {
    SQLExpr_ptr expr = orderByItem->getExpr();
    if (instanceof <SQLExpr, SQLName>(expr) && SQLUtils::nameEquals(std::dynamic_pointer_cast<SQLName>(expr), columnName))
    {
      orderByItem->setExpr(to->getName()->clone());
      return BOOL::TRUE;
    }

    if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr) &&
                       SQLUtils::nameEquals(std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr)->getMethodName(), columnName->getSimpleName()))
    {
      // More complex when with key length.
      std::list<SQLExpr_ptr>::iterator it = (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr).get())->getArguments()->begin();
      advance(it, 0);
      if (1 == (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr).get())->getArguments()->size() && (instanceof <SQLExpr, SQLIntegerExpr>(*it)))
      {
        std::list<SQLExpr_ptr>::iterator it = to->getDataType()->getArguments()->begin();
        advance(it, 0);
        if (to->getDataType()->hasKeyLength() && 1 == to->getDataType()->getArguments()->size() && instanceof <SQLExpr, SQLIntegerExpr>(*it))
        {

          int newKeyLength = (int)(std::dynamic_pointer_cast<SQLIntegerExpr>(*it))->getNumber();
          std::list<SQLExpr_ptr>::iterator it_1 = (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr))->getArguments()->begin();
          advance(it_1, 0);
          int oldKeyLength = (int)(std::dynamic_pointer_cast<SQLIntegerExpr>(*it_1))->getNumber();
          if (newKeyLength > oldKeyLength)
          {
            // Change name and keep key length.
            string_ptr tmp = to->getName()->getSimpleName();
            (std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr))->setMethodName(tmp);
            return BOOL::TRUE;
          }
        }
        // Remove key length.
        orderByItem->setExpr(to->getName()->clone());
        return BOOL::TRUE;
      }
    }
  }
  return BOOL::FALSE;
}

BOOL_ptr SQLUnique::applyDropColumn(SQLName_ptr columnName)
{
  for (int i = getColumns()->size() - 1; i >= 0; i--)
  {
    std::list<SQLSelectOrderByItem_ptr>::iterator it = getColumns()->begin();
    std::advance(it, i);
    SQLExpr_ptr expr = (*it)->getExpr();
    if (instanceof <SQLExpr, SQLName>(expr) && SQLUtils::nameEquals(std::dynamic_pointer_cast<SQLName>(expr), columnName))
    {
      // getColumns().remove(i);

      std::list<SQLSelectOrderByItem_ptr>::iterator pnlist = getColumns()->begin();
      std::advance(pnlist, i);           // 迭代器前进i个元素，注意i是从0开始
      if (pnlist != getColumns()->end()) // 最后一个不能删除
        getColumns()->erase(pnlist);     // 删除第i个元素

      return BOOL::TRUE;
    }

    if (instanceof <SQLExpr, SQLMethodInvokeExpr>(expr) &&
                       SQLUtils::nameEquals((std::dynamic_pointer_cast<SQLMethodInvokeExpr>(expr))->getMethodName(), columnName->getSimpleName()))
    {
      // getColumns().remove(i);

      std::list<SQLSelectOrderByItem_ptr>::iterator pnlist = getColumns()->begin();
      std::advance(pnlist, i);           // 迭代器前进i个元素，注意i是从0开始
      if (pnlist != getColumns()->end()) // 最后一个不能删除
        getColumns()->erase(pnlist);     // 删除第i个元素

      return BOOL::TRUE;
    }
  }
  return BOOL::FALSE;
}

SQLName_list_ptr SQLUnique::getCovering()
{
  return indexDefinition->getCovering();
}
